Undo
Volume Number: 3
Issue Number: 5
Column Tag: Programmer's Workshop
Implementing Undo For Text Edit 
By Melvyn D. Magree, (M)agreeable software, inc.
Undo (Cmd-Z) It!
Honored more in the breech than not is the standard Edit Menu Apple published in
Inside Macintosh (page I-58). Even if a program does contain an Edit Menu with the
recommended items, Undo might not really be available.
An Undo command is a very helpful feature, e specially if you just did Clear when
you meant to do Cut or if you backspaced one word too many or whatever! It is
remarkable that Undo is not included in all editing type programs because it is not that
difficult to program. I designed and coded a text-file version for my own development
system in less that two weeks. I modeled my Undo by observing the behavior of
MacWrite.
We want to give the user the following Undo capabilities:
Undo Cut:
• Reinsert the cut text from the clipboard.
• Restore the previous contents of the clipboard.
Undo Copy:
• Restore the previous contents of the clipboard.
Undo Paste:
• Remove pasted text.
• Reinsert the text overlaid by Paste.
Undo Clear:
• Reinsert the text removed by Clear.
Undo Typing:
• Remove all characters typed since the user made the last selection.
• Reinsert the selection overlaid by the typing including any removed by
backspacing.
For each Undo operation except copy, we also must reset the selection as it was
before the user requested the "undid" operation.
Redo Cut, Copy, Paste, and Clear are straightforward. We merely perform a Cut,
Copy, Paste, or Clear. However, for Redo Typing, we must:
• Remove the current selection.
• Remove any characters that the user had backspaced over.
• Reinsert the "effective" typing sequence that the user Undid.
The effective typing sequence is the string of characters remaining after the user
backspaced. That is, if the text and selection had been as follows:
and the user had typed:
{backspace}{backspace}the most user-
dis{backspace}{backspace}{backspace}un
resulting in
then the effective typing sequence is
the most user-un
If the user requested Undo Typing, the text and selection would revert to
and if the user followed the request with Redo Typing, the text and selection would once
again become:
To make sure that we can undo an operation, we have to save any information that
the operation destroys. So, our edit operations are:
Cut
• Save current contents of clipboard.
• Cut current selection to clipboard.
• Set menu to Undo Cut.
• Save current contents of clipboard,
• Copy current selection to clipboard, and
• Set menu to Undo Copy.
• Save current selection,
• Paste clipboard to current selection, and
• Set menu to Undo Paste.
Clear
• Save current selection,
• Delete current selection, and
• Set menu to Undo Clear.
Typing
• If first character for selection:
Save current selection.
• If backspace over previously unsaved character:
Insert unsaved character at beginning of saved selection.
• Insert character in text at insertion point.
• Set menu to Undo Typing.
Note that I use the term clipboard to refer to the TextEdit scrap, not the clipboard
file. To actually put the TextEdit scrap in the clipboard file (also called the desk
scrap) you must call TEtoScrap. To read the clipboard to the TextEdit scrap you must
call TEfromScrap.
The key design elements for undo (and redo) are a state variable for the next
possible undo operation and a second Text Edit record. We use the state variable to
reset the Undo item of the Edit Menu and to call the proper procedure when the user
requests Undo. We use the second Text Edit record as a private scrap area to save the
text removed by the user's last editing operation. We can then use the various TextEdit
procedures to move text from the clipboard to the private scrap or vice versa.
To try out some simple versions of these undoable editing routines, we will write
a very simple editing program. The program allows the user to select a file, opens a
window, reads the contents of the file into the window, and then allows the user to
change the contents of the window by typing, by using the mouse, and by selecting from
the Edit menu. The program allows the user to close the window and open another from
the File menu. The program's File menu also contains a Quit entry.
If you are familiar with writing Macintosh stand-alone applications, you might
want to skip over the next section to Editing Functions.
To keep the program simple, we will ignore many expected Macintosh features
such as desk accessories, moving the window on the screen, scrolling the text, and
making the program easily translatable from English. We will not check for many
possible error situations.
Thus, our main program is:
Initialize tool box,
Initialize program's global variables,
Initialize menus, and
Start the main event loop.
Our main event loop checks only for four events:
activate:
If window open calls TEActivate.
mouse down:
If in menu, calls menu selector
Else if window open calls TE selector.
key down and auto key:
If window open calls typing routine
Our main event loop continues to look for these events until the menu selector
sets the global variable Quit to TRUE. When Quit is TRUE, the main event loop returns
to our main program which terminates.
Our menu selector:
Determines which menu item was selected,
If Apple menu, ignores it,
If File Menu, calls File Manager with item, or
If Edit Menu, calls Edit Manager with the item.
Removes highlighting from menu bar.
Our File Manager calls FileOpen or FileClose according to the item selected. That
is,
If Open, calls Open_File,
If Close, calls Close_File, and
If Quit, then
If a file is open, calls Close_File,
Sets QuitFlag to TRUE.
Open_File
Asks the user to select a file from a list;
Opens the requested file;
Opens a window to display the text of the file;
Opens a TextEdit record to control display of the text;
Opens a TextEdit record for the scrap;
Reads text of the file into TextEdit record;
Disables the Open item and enables Close item;
The only error condition that we will check is if the user clicked the cancel
button in the file dialog box. Note that if the file contains more than 32,767
characters, then our program may hang. TEInsert does not check for this limit and
may never return. [The 32K limit is notorious throughout Text Edit, e specially in
TEScroll. This has not been changed in the SE & Mac II ROMS unfortunately. -Ed]
Close_File does almost the reverse of Open_File. It:
Closes the display TextEdit record,
Closes the scrap TextEdit record,
Closes the window,
Closes the file, and
Enables the Open item and disables Close item.
If we allowed the user to actually change the file, then we would have to rewrite
the text and flush the volume also. Without considering the editing portion of our
program, we need the following global variables:
Pointer for the window,
Handle for the TextEdit record,
Handle for the scrap TextEdit record,
Integer for the file reference number, and
Boolean for the Quit flag.
Our first program in TML Pascal is then shown in listing one.
PROGRAM UndoIt;
{Program to test Edit menu including Undo/Redo of previous operation}
{$L UndoIt/Rsrc}
{$I Memtypes.Ipas }
{$I QuickDraw.Ipas}
{$I OSIntf.Ipas }
{$I ToolIntf.Ipas }
{$I PackIntf.Ipas }
CONST MenuBarID = 200;
FileMenu = 200;
OpenItem = 1;
CloseItem = 2;
QuitItem = 4;
EditMenu = 201;
UndoItem = 1;
CutItem = 3;
CopyItem = 4;